{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit ManagerForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgList, Menus, ComCtrls, ToolWin, ActnList, MediaFolderManager,
  MediaFolders, PngImageList;

const
  MSG_CONFIRM_SAVE = 'Your changes have not been saved. Would you like to save ' +
                     'your modified media folders before closing?';
  MSG_CONFIRM_DEL = 'Are you sure want to remove this directory from the media ' +
                    'repository?' + sLineBreak + sLineBreak + 'Once removed you' +
                    ' will not be able to view any of the media within mediate.' +
                    sLineBreak + sLineBreak + 'Note: This does not delete your ' +
                    'actual files on disk.';
  MSG_CONFIRM_IMPORT = 'Your mediate media database may be missing or out of date. ' +
                       'Would you like to import media details into the media database? ' +
                       '(This may take a few minutes)';
  MSG_WARN_SAVE_FAIL = 'Failed to save media database. This may be because mediate ' +
                       'is currently running and is using the media database. ' +
                       'Would you like to close mediate and try again?';

type
  TfrmMediaFolders = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
    Exit1: TMenuItem;
    tabFolders: TTabControl;
    lvFolders: TListView;
    ActionList1: TActionList;
    acExit: TAction;
    acEdit: TAction;
    acSave: TAction;
    acSave1: TMenuItem;
    acNewMusic: TAction;
    acNewVideo: TAction;
    acNewImage: TAction;
    Addpicturesdirectory1: TMenuItem;
    Addmusicdirectory1: TMenuItem;
    Addvideodirectory1: TMenuItem;
    N1: TMenuItem;
    N2: TMenuItem;
    acAbout: TAction;
    acDelete: TAction;
    acScanMedia: TAction;
    ools1: TMenuItem;
    Scanmediafiles1: TMenuItem;
    tbTopToolbar: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton7: TToolButton;
    ToolButton10: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    acSearchCovers: TAction;
    ToolButton8: TToolButton;
    Searchforcovers1: TMenuItem;
    procedure acScanMediaUpdate(Sender: TObject);
    procedure acSearchCoversUpdate(Sender: TObject);
    procedure acSearchCoversExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure acExitExecute(Sender: TObject);
    procedure tabFoldersChange(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure acNewFolderExecute(Sender: TObject);
    procedure lvFoldersCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure lvFoldersColumnClick(Sender: TObject; Column: TListColumn);
    procedure acDeleteExecute(Sender: TObject);
    procedure lvFoldersClick(Sender: TObject);
    procedure lvFoldersChange(Sender: TObject; Item: TListItem;
      Change: TItemChange);
    procedure acEditExecute(Sender: TObject);
    procedure acAboutExecute(Sender: TObject);
    procedure acSaveExecute(Sender: TObject);
    procedure acScanMediaExecute(Sender: TObject);
  private
    { Private declarations }
    FSortColumn: Integer;
    FSortDescending: Boolean;
    FListUpdating: Boolean;
    FToolbarImages: TPngImageList;
    FManager: TAHMMediaFolderManager;
    FWorking: Boolean;
    function CurrentType: TAHMMediaFolderType;
    procedure DeleteLocation(Folder: TAHMMediaFolder);
    procedure AddLocation(const FolderName, FolderPath: String);
    procedure DisplayLocations;
    procedure UpdateGUIState;
    procedure DroppedFiles(var Message: TWMDROPFILES); message WM_DROPFILES;
    procedure LoadImages;
  protected
    { Protected declarations }
    procedure WMSysCommand(var Msg: TWmSysCommand); message WM_SYSCOMMAND;
    procedure CreateParams(var Params: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  frmMediaFolders: TfrmMediaFolders;

implementation

{$R *.dfm}

uses FormatPrefs, FolderForm, MediaImportForm, CoverListForm, BaseAbout,
     Utilities, ShellAPI;

procedure TfrmMediaFolders.CreateParams(var Params: TCreateParams);
begin
  // Tweak window flags to make this appear as an app window on taskbar
  inherited CreateParams(Params);

  Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or WS_EX_APPWINDOW;
end;

procedure TfrmMediaFolders.FormCreate(Sender: TObject);
begin
  // Fix extra taskbar window under Vista
  RemoveApplicationWindowFromTaskBar;

  // Create long term manager object for media folders
  FManager := TAHMMediaFolderManager.Create;

  // Set default sort order (by name)
  FSortColumn := 1;

  // Create and populate PNG image list
  FToolbarImages := TPngImageList.Create(Self);
  tbTopToolbar.Images := FToolbarImages;
  LoadImages;

  // Load and display locations when we start
  FManager.LoadLocations;
  DisplayLocations;

  // Inform Windows that we will accept drag & dropped folders
  DragAcceptFiles(Handle, True);
end;

procedure TfrmMediaFolders.FormDestroy(Sender: TObject);
begin
  // Free long term media folder manager object
  FManager.Free;
end;

procedure TfrmMediaFolders.WMSysCommand(var Msg: TWmSysCommand);
begin
  // Override standard delphi handling to get proper effects under Vista
  case (Msg.CmdType and $FFF0) of
    SC_MINIMIZE:
    begin
      ShowWindow(Handle, SW_MINIMIZE);
      Msg.Result := 0;
    end;
    SC_RESTORE:
    begin
      ShowWindow(Handle, SW_RESTORE);
      Msg.Result := 0;
    end;
  else
    inherited;
  end;
end;

procedure TfrmMediaFolders.acExitExecute(Sender: TObject);
begin
  // Check media folders have been saved
  if FManager.Modified then
    case MessageDlg(MSG_CONFIRM_SAVE, mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: FManager.SaveLocations;
      mrCancel: Exit;
    end;

  // Check media database has been imported
  if FManager.ImportRequired then
    case MessageDlg(MSG_CONFIRM_IMPORT, mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: acScanMedia.Execute;
      mrCancel: Exit;
    end;

  // Close application
  Close;
end;

procedure TfrmMediaFolders.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  // Check media folders have been saved
  if FManager.Modified then
    case MessageDlg(MSG_CONFIRM_SAVE, mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: FManager.SaveLocations;
      mrCancel: CanClose := False;
    end;

  // Check media database has been imported
  if FManager.ImportRequired then
    case MessageDlg(MSG_CONFIRM_IMPORT, mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: acScanMedia.Execute;
      mrCancel: CanClose := False;
    end;
end;

procedure TfrmMediaFolders.tabFoldersChange(Sender: TObject);
begin
  // Display new list when we change tabs
  DisplayLocations;
end;

procedure TfrmMediaFolders.lvFoldersColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  // Sort list contents by clicked column. Toggle between descending
  // and ascending sort if same column is clicked again
  if FSortColumn = Column.Index then
    FSortDescending := not FSortDescending
  else
  begin
    FSortColumn := Column.Index;
    FSortDescending := False;
  end;
  lvFolders.AlphaSort;
end;

procedure TfrmMediaFolders.lvFoldersCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  i: Integer;
begin
  // Compare appropriate column
  if FSortColumn = 0 then
    if Item1.Checked = Item2.Checked then Compare := 0
    else if Item1.Checked then Compare := -1
    else Compare := 1
  else
  begin
   i := Pred(FSortColumn);
   Compare := CompareText(Item1.SubItems[i], Item2.SubItems[i]);
  end;

  // Invert comparison for descending search
  if FSortDescending then Compare := -Compare;
end;

procedure TfrmMediaFolders.lvFoldersClick(Sender: TObject);
begin
  // Keep gui consistent when we select/unselect a directory
  if not FListUpdating then UpdateGUIState;
end;

procedure TfrmMediaFolders.lvFoldersChange(Sender: TObject;
  Item: TListItem; Change: TItemChange);
var
  Folder: TAHMMediaFolder;
begin
  // Handle checked changes - persist them in manager
  if (not FListUpdating) and Assigned(Item) and (Change = ctState) then
  begin
    Folder := TAHMMediaFolder(Item.Data);
    if Folder.VirtualFolder <> Item.Checked then
    begin
      Folder.VirtualFolder := Item.Checked;
      FManager.Modified := True;

      // Ensure GUI is in a consistent state
      UpdateGUIState;
    end;
  end;
end;

procedure TfrmMediaFolders.acSaveExecute(Sender: TObject);
begin
  FManager.SaveLocations;

  // Ensure GUI is in a consistent state
  UpdateGUIState;
end;

procedure TfrmMediaFolders.acNewFolderExecute(Sender: TObject);
begin
  // Switch to appropriate tag for selected folder type
  tabFolders.TabIndex := (Sender as TAction).Tag;

  // Configure folder form for adding new folder
  with frmFolderDetails do
  begin
    Mode := fmAdd;
    PopupParent := Self;
    FolderType := CurrentType;
    if ShowModal = mrOK then
    begin
      FManager.Locations.Folders[CurrentType].AddFolder(Folder);
      FManager.Modified := True;
      DisplayLocations;
    end;
  end;
end;

procedure TfrmMediaFolders.acDeleteExecute(Sender: TObject);
var
  Folder: TAHMMediaFolder;
begin
  // Confirm delete then reload list view
  if MessageDlg(MSG_CONFIRM_DEL, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    // Delete folder item for selected group
    Folder := TAHMMediaFolder(lvFolders.Selected.Data);
    DeleteLocation(Folder);
  end;
end;

procedure TfrmMediaFolders.acEditExecute(Sender: TObject);
begin
  // Validate that we have a selected list item
  if not Assigned(lvFolders.Selected) then Exit;

  with frmFolderDetails do
  begin
    Mode := fmEdit;
    PopupParent := Self;
    FolderType := CurrentType;
    Folder := TAHMMediaFolder(lvFolders.Selected.Data);
    if ShowModal = mrOK then
    begin
      FManager.Modified := True;
      DisplayLocations;
    end;
  end;
end;

procedure TfrmMediaFolders.acScanMediaExecute(Sender: TObject);
begin
  // Disable actions whilst importing
  FWorking := True;
  try
    // Determine which media formats we should import
    TAHMFormatPreferences.LoadFormats(FManager.ConfigPath);
    FManager.CreateDatabase;

    // Display media import dialog
    if TfrmMediaImport.ImportMedia(FManager.Repository, False) then
    begin
      // Attempt to save temporary database to live
      while not FManager.SaveDatabase do
        if MessageDlg(MSG_WARN_SAVE_FAIL, mtWarning, [mbYes, mbNo], 0) = mrNo then
          Break
        else
        begin
          // Failed.. close mediate and try again
          Screen.Cursor := crHourglass;
          FManager.CloseMediate;
          Screen.Cursor := crDefault;
        end;
    end;

    FManager.CloseDatabase;
  finally
    FWorking := False;
  end;
end;

procedure TfrmMediaFolders.acScanMediaUpdate(Sender: TObject);
begin
  // Disable scan media action whilst busy on a thread
  acScanMedia.Enabled := not FWorking;
end;

procedure TfrmMediaFolders.acSearchCoversExecute(Sender: TObject);
begin
  // Disable actions whilst searching
  FWorking := True;
  try
    // Configure internet proxy settings for searching
    TAHMFormatPreferences.LoadFormats(FManager.ConfigPath);
    
    if FManager.OpenDatabase then
    begin
      // Display cover list dialog
      TfrmCoverList.ViewCovers(FManager.Repository);
    end;
  finally
    FManager.CloseDatabase;
    FWorking := False;
  end;
end;

procedure TfrmMediaFolders.acSearchCoversUpdate(Sender: TObject);
begin
  // Cover search only allowed once media has been imported and not busy
  acSearchCovers.Enabled := not (FWorking or FManager.ImportRequired);
end;

procedure TfrmMediaFolders.acAboutExecute(Sender: TObject);
begin
  // Create and display about form
  TfrmBaseAbout.ShowAboutBox('This application allows media folders to be ' +
                             'configured for use within mediate.');
end;

//----------------------------------------------------------------------------//

function TfrmMediaFolders.CurrentType: TAHMMediaFolderType;
begin
  // Use tab selection to determine current folder type
  case tabFolders.TabIndex of
    0: Result := ftMusic;
    1: Result := ftVideo;
    else Result := ftImage;
  end;
end;

procedure TfrmMediaFolders.DeleteLocation(Folder: TAHMMediaFolder);
var
  List: TAHMMediaFolderList;
begin
  // Determine appropriate folder list to perform deletion
  List := FManager.Locations.Folders[CurrentType];
  List.DeleteFolder(Folder);
  FManager.Modified := True;

  DisplayLocations;
end;

procedure TfrmMediaFolders.AddLocation(const FolderName, FolderPath: String);
var
  i: Integer;
  Folder: TAHMMediaFolder;
begin
  // First check that we don't already have passed Folder listed
  with FManager.Locations.Folders[CurrentType] do
  begin
    for i := 0 to Pred(Count) do
    begin
      Folder := Folders[i];
      if (Folder.FolderName = FolderName) and (Folder.FolderPath = FolderPath) then Exit;
    end;

    // Okay to add passed folder - it's unique
    AddFolder(FolderName, FolderPath, False);
  end;

  FManager.Modified := True;
end;

procedure TfrmMediaFolders.DisplayLocations;
var
  List: TAHMMediaFolderList;
  i: Integer;
begin
  with lvFolders do
  begin
    FListUpdating := True;
    Items.BeginUpdate;
    try
      Items.Clear;

      // Determine appropriate folder list to populate
      List := FManager.Locations.Folders[CurrentType];

      for i := 0 to Pred(List.Count) do
        with Items.Add do
        begin
          Data := List.Folders[i];
          Checked := List.Folders[i].VirtualFolder;
          SubItems.Add(List.Folders[i].FolderName);
          SubItems.Add(List.Folders[i].FolderPath);
        end;

      lvFolders.AlphaSort;
    finally
      Items.EndUpdate;
      FListUpdating := False;
    end;
  end;

  // Ensure GUI is in a consistent state
  UpdateGUIState;
end;

procedure TfrmMediaFolders.UpdateGUIState;
begin
  // Enable/Disable actions depending on current selection
  acEdit.Enabled := Assigned(lvFolders.Selected);
  acDelete.Enabled := Assigned(lvFolders.Selected);
  acSave.Enabled := FManager.Modified;
end;

procedure TfrmMediaFolders.DroppedFiles(var Message: TWMDROPFILES);
var
  i, Count: LongInt;
  Buffy: array[0..MAX_PATH] of char;
  Name: String;
begin
  // Pass $ffffffff to retrieve file count
  Count := DragQueryFile(Message.Drop, $ffffffff, nil, 0);

  // Iterate through each dropped file looking for directories
  for i := 0 to Pred(Count) do
    if (DragQueryFile(Message.Drop, i, @Buffy, SizeOf(Buffy)) > 0) then
    begin
      if ExtractFileExt(Buffy) <> '' then Continue;
      Name := ExtractFileName(Buffy);
      AddLocation(Name, Buffy);
    end;

  // Refresh to show new locations
  DisplayLocations;
end;

procedure TfrmMediaFolders.LoadImages;
begin
  with FToolbarImages do
  begin
    Clear;
    Width := 32; Height := 32;

    BeginUpdate;
    AddResourcePng('LG_IMG_NEW_MUSIC');    //0
    AddResourcePng('LG_IMG_NEW_VIDEO');    //1
    AddResourcePng('LG_IMG_NEW_PICTURE');  //2
    AddResourcePng('LG_IMG_DELETE');       //3
    AddResourcePng('LG_IMG_SAVE');         //4
    AddResourcePng('LG_IMG_EDIT');         //5
    AddResourcePng('LG_IMG_EXIT');         //6
    AddResourcePng('LG_IMG_ABOUT');        //7
    AddResourcePng('LG_IMG_SEARCH');       //8
    AddResourcePng('LG_IMG_COVERS');       //9
    EndUpdate;
  end;
end;


end.
